const path = require('path');

const multer = require('multer');
// const multiparty = require('multiparty');// 需通过listener监听得到参数
const multiparty = require('connect-multiparty');

// NODE文档: http://nodejs.cn/api/fs.html
// const fs = require('fs');
// const { promisify } = require('util');
const fse = require('fs-extra');

// console.log(module.parent);

const oTemp = path.join(__dirname, '../public/temp');
const multipartMiddleware = multiparty();

// const upload = multer({ dest: 'uploads/' });
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, path.join(__dirname, '../public/images'));
  },
  filename: (req, file, cb) => {
    const uniqueSuffix = `${Date.now()}-${Math.round(Math.random() * 1E9)}`;
    cb(null, `${file.fieldname}-${uniqueSuffix}-${file.originalname}`);
  },
});
const multerMiddleware = multer({ storage });

const multerHandler = async (req, res, next) => {
  try {
    // const { fieldname, originalname, filename, path } = req.file;
    console.log(req.file);
    res.json({
      code: 0,
      msg: 'multer上传方式',
    });
  } catch (error) {
    next(error);
  }
};

const multipartyHandler = async (req, res, next) => {
  try {
    const { md5, index } = req.body;
    // new multiparty.Form.uploadDir默认是`path: 'C:\\Users\\admin\\AppData\\Local\\Temp\\上传的文件',`
    // don't forget to delete all req.files when done
    const { file } = req.files;
    const chunkDir = `${oTemp}/${md5}`;
    const chunkFile = `${chunkDir}/${index}`;

    const isExistFile = await fse.pathExists(chunkFile);
    // const isExistFile = await promisify(fs.access)(chunkFile, fs.constants.F_OK);
    if (isExistFile) {
      await fse.remove(file.path);// 删除暂存
      res.json({
        code: 0,
        msg: '已存在',
        data: null,
      });
      return;
    }
    const isExistDir = await fse.pathExists(chunkDir);
    if (!isExistDir) {
      // 不存在创建成功返回undefined; 存在还创建报错
      await fse.mkdirs(chunkDir);
    }

    // overwrite: 覆盖现有文件或目录，默认值为false
    await fse.move(file.path, chunkFile, { overwrite: false });
    res.json({
      code: 0,
      msg: '上传成功',
      data: null,
    });
  } catch (error) {
    next(error);
  }
};

/**
 * [三种方式](https://www.cnblogs.com/goloving/p/12825973.html)
 * 方式一: buffer合并(占内存)
 */
const mergeBufferHandler = async (req, res, next) => {
  try {
    const { md5, name } = req.params;
    const mergeDir = `${oTemp}/${md5}`;
    const isExistDir = await fse.pathExists(mergeDir);
    if (!isExistDir) {
      res.json({
        code: 0,
        msg: '未找到合并项',
        data: null,
      });
      return;
    }

    const aFilePath = await fse.readdir(mergeDir);
    const targetPath = path.join(__dirname, '../public/zip', name);
    const isTargetFile = await fse.pathExists(targetPath);
    if (isTargetFile) {
      aFilePath.forEach(item => {
        fse.unlinkSync(`${mergeDir}/${item}`);
      });
      // 删除空目录
      await fse.rmdir(mergeDir);
      res.json({
        code: 0,
        msg: '合并项已存在',
        data: {
          url: `http://localhost:7003/zip/${name}`,
        },
      });
      return;
    }

    // readFile 得到 buffer
    const aBuffers = aFilePath.map(item => fse.readFileSync(`${mergeDir}/${item}`));
    // Buffer.concat 合并
    const concatBuffer = Buffer.concat(aBuffers);
    // writeFile 写入 buffer 到文件
    await fse.writeFile(targetPath, concatBuffer);
    // 删除切片
    aFilePath.forEach(item => {
      fse.unlinkSync(`${mergeDir}/${item}`);
    });
    // 删除空目录
    await fse.rmdir(mergeDir);
    res.json({
      code: 0,
      msg: '合并成功',
      data: {
        url: `http://localhost:7003/zip/${name}`,
      },
    });
  } catch (error) {
    next(error);
  }
};

/**
 * 方式二: 流合并方式
 * 首先通过fs.createWriteStream()创建了一个可写流，用来存放最终合并的文件。然后使用fs.createReadStream()分别读取各个分片后的文件，再通过pipe()方式将读取的数据像倒水一样“倒”到可写流中，到监控到一杯水倒完后，马上接着倒下一杯，直到全部倒完为止。
 *
 * 方式三：追加文件方式合并
 * fs.appendFile()的作用是异步地追加数据到一个文件，如果文件不存在则创建文件。data可以是一个字符串或buffer。
 * 性能强过buffer合并方式，但不及流合并方式。
 */
const pipeStream = (chunkPath, writeStream) => (
  new Promise(resolve => {
    const readStream = fse.createReadStream(chunkPath);
    readStream.pipe(writeStream, { end: false });
    readStream.on('end', () => {
      // 删除切片文件
      fse.unlinkSync(chunkPath);
      resolve();
    });
  })
);
/**
 * 合并流(封装): Promise 和 递归调用
 */
const mergeHandler = async (req, res, next) => {
  try {
    const { size, name } = req.params;

    const aChunkPaths = res.locals.aFilePath.map(item => `${res.locals.mergeDir}/${item}`);
    const aChunksPromise = aChunkPaths.map((chunkPath, index) => {
      if (!size) {
        return pipeStream(chunkPath, fse.createWriteStream(res.locals.targetPath));
      }
      return pipeStream(chunkPath, fse.createWriteStream(res.locals.targetPath, {
        start: index * size,
        end: (index + 1) * size,
      }));
    });
    await Promise.all(aChunksPromise);
    // 删除空目录
    fse.rmdirSync(res.locals.mergeDir);
    // delete res.locals

    res.json({
      code: 0,
      msg: '合并成功',
      data: {
        url: `http://localhost:7003/zip/${name}`,
      },
    });
  } catch (error) {
    next(error);
  }
};

/**
 * 判断是否存在需合并的切片
 */
const findMergeHandler = async (req, res, next) => {
  try {
    const { md5 } = req.params;
    const mergeDir = `${oTemp}/${md5}`;
    res.locals.mergeDir = mergeDir;

    const isExistDir = await fse.pathExists(mergeDir);
    if (!isExistDir) {
      // delete res.locals
      res.json({
        code: 0,
        msg: '未找到合并项',
        data: null,
      });
      return;
    }
    next();
  } catch (error) {
    next(error);
  }
};

/**
 * 判断是否存在 已合并文件
 */
const findTargetHandler = async (req, res, next) => {
  try {
    const { md5, name } = req.params;
    const mergeDir = `${oTemp}/${md5}`;

    const aFilePath = await fse.readdir(mergeDir);
    const targetPath = path.join(__dirname, '../public/zip', name);
    res.locals.aFilePath = aFilePath;
    res.locals.targetPath = targetPath;
    const isTargetFile = await fse.pathExists(targetPath);
    if (isTargetFile) {
      // delete res.locals
      aFilePath.forEach(item => {
        fse.unlinkSync(`${mergeDir}/${item}`);
      });
      // 删除空目录
      await fse.rmdir(mergeDir);
      res.json({
        code: 0,
        msg: '合并项已存在',
        data: {
          url: `http://localhost:7003/zip/${name}`,
        },
      });
      return;
    }
    next();
  } catch (error) {
    next(error);
  }
};

/**
 * 验证是否 已上传 / 已上传切片de下标
 */
const verifyUploadHandler = async (req, res, next) => {
  const { fileHash, filename } = req.body;
  const ext = filename.slice(filename.lastIndexOf('.'), filename.length);
  const filePath = path.resolve(oTemp, `${fileHash}${ext}`);
  if (fse.existsSync(filePath)) {
    res.end(
      JSON.stringify({
        shouldUpload: false,
      }),
    );
  } else {
    res.end(
      JSON.stringify({
        shouldUpload: true,
        // uploadedList: await createUploadedList(fileHash),
      }),
    );
  }
};

module.exports = {
  multerMiddleware,
  multerHandler,

  multipartMiddleware,
  multipartyHandler,

  mergeBufferHandler,

  findMergeHandler,
  findTargetHandler,
  mergeHandler,

  verifyUploadHandler,
};
